package org.aldar.bquest.entity;

import org.aldar.bquest.constants.IConstants;
import org.aldar.bquest.constants.LandscapeType;
import org.aldar.bquest.effects.MoveSmoothModifier;
import org.aldar.bquest.effects.SymbolAnimationUtil;
import org.aldar.bquest.entity.behaviour.AfterMatchEntityModifierListener;
import org.aldar.bquest.entity.effect.ParticleEmitter;
import org.aldar.bquest.entity.effect.ParticleUtil;
import org.aldar.bquest.entity.pool.TilesPool;
import org.aldar.bquest.logic.controller.board.AfterMatchProcessor;
import org.aldar.bquest.logic.controller.board.TouchAndMoveController;
import org.aldar.bquest.logic.controller.entity.HeroAnimationController;
import org.aldar.bquest.logic.controller.entity.TileBoard;
import org.aldar.bquest.logic.controller.level.LevelController;
import org.aldar.bquest.logic.controller.player.ManaAndPointsController;
import org.aldar.bquest.logic.layout.ScreenContentManager;
import org.aldar.bquest.profile.GameProfile;
import org.aldar.bquest.resources.ResourceManager;
import org.aldar.bquest.screens.MainBoardScreen;
import org.aldar.bquest.util.Logger;
import org.aldar.bquest.util.SpriteUtil;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.entity.Entity;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.*;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.util.modifier.IModifier;

import java.util.Random;

import static org.aldar.bquest.entity.effect.ParticleUtil.showStarRotationEffect;

/**
 * User: Daria
 * Date: 6/16/13 12:06 AM
 */
public class AbilityTileSprite extends ManaIncreasingTile {

    private static final Logger LOG = new Logger(AbilityTileSprite.class);
    private Sprite abilityAbove;

    private static float DURATION_SCALE_UP_CRYSTAL_ON_ACTIVATE = 0.5f;
    private static float DURATION_FADE_OUT_CRYSTAL_ON_ACTIVATE = 0.3f;
    private static float DURATION_ROTATE_STAR_ON_ACTIVATE =
            DURATION_FADE_OUT_CRYSTAL_ON_ACTIVATE + DURATION_SCALE_UP_CRYSTAL_ON_ACTIVATE;

    public enum AbilityType {
        MOVE_HERO,
        MISSILE(true),
        EXPLODE_9,
        COLLECT_CRYSTALS,
        PLAY_CONCERT(true);
        private boolean requiresHeroActions = false;

        AbilityType(boolean requiresHeroActions) {
            this.requiresHeroActions = requiresHeroActions;
        }

        AbilityType() {
        }

        public boolean requiresHeroActions() {
            return requiresHeroActions;
        }
    }

    private AbilityType originalAbility;

    private AbilityType abilityType;

    public static AbilityTileSprite getRandomAbilityTileSprite(int col, int row) {
        AbilityType abilityType = getRandomAbilityTypeNotConcert();
        LOG.i("got random ability " + abilityType.name());
        return getAbilityTileSprite(col, row, abilityType);
    }

    public static AbilityTileSprite getPlayConcertTileSprite(int col, int row) {
        return getAbilityTileSprite(col, row, AbilityType.PLAY_CONCERT);
    }

    public static AbilityTileSprite getAbilityTileSprite(int col, int row, AbilityType abilityType) {
        ITextureRegion textureRegion = ResourceManager.getInstance().getAbilityTileTextureRegion(abilityType);
        AbilityTileSprite sprite = new AbilityTileSprite(col, row, textureRegion, MainBoardScreen.getScm().getVertexBufferObjectManager(), false);
        sprite.abilityType = abilityType;
        return sprite;
    }

    protected AbilityTileSprite(int col, int row, ITextureRegion mTileTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager, boolean isConcert) {
        super(col, row, isConcert ? mTileTextureRegion : ResourceManager.getInstance().getCristalBigRegion(), pVertexBufferObjectManager);
//        if (!isConcert) {
        abilityAbove = TilesPool.getEntityForTexture(mTileTextureRegion);
        abilityAbove.setAnchorCenter(0.5f, 0.5f);
        abilityAbove.setPosition(50, 60);
        getLsSprite().setAnchorCenter(0.5f, 0.5f);
        getLsSprite().setPosition(50, 60);
        this.getTile().attachChild(abilityAbove);
  /*      } else {
            //adjust concert icon position
            getLsSprite().setAnchorCenter(0.5f, 0.5f);
            getLsSprite().setPosition(50,60);
        }
        */
    }

    public static AbilityType getRandomAbilityTypeNotConcert() {


        int abilityIndex = 0;
        LOG.i("AbilityTypes for current level: " + GameProfile.getCurrentLevel().getAbilityTypes());
        if (GameProfile.getCurrentLevel().getAbilityTypes().length == 1) {
            abilityIndex = 0;
        } else {
            abilityIndex = new Random().nextInt(GameProfile.getCurrentLevel().getAbilityTypes().length);
        }
        LOG.i("Chosen random ability index: " + abilityIndex);
        return GameProfile.getCurrentLevel().getAbilityTypes()[abilityIndex];
    }

    @Override
    public boolean isDragDropAllowed() {
        return true;
    }

    public void replaceAbility(AbilityType newAbility) {
        setOriginalAbility(abilityType);
        setAbilityType(newAbility);
        //getLsSprite().detachSelf();
        abilityAbove.detachSelf();
        final Sprite newAbilitySprite = new Sprite(0, 0,
                ResourceManager.getInstance().getAbilityTileTextureRegion(newAbility),
                MainBoardScreen.getScm().getVertexBufferObjectManager());
//        newAbilitySprite.setAnchorCenter(0, 0);
        newAbilitySprite.setAnchorCenter(0.5f, 0.5f);
        newAbilitySprite.setPosition(50, 60);
        newAbilitySprite.setAlpha(1f);

        getTileGroundContainer().attachChild(newAbilitySprite);
//        setLsSprite(newAbilitySprite);
        abilityAbove = newAbilitySprite;
        newAbilitySprite.registerEntityModifier(new ScaleModifier(0.2f, 1f, 2f, new IEntityModifier.IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                //To change body of implemented methods use File | Settings | File Templates.
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                newAbilitySprite.registerEntityModifier(new ScaleModifier(0.2f, 2f, 1f));
            }
        }));
    }

    public void performAbility(int col, int row) {
        detachFromLayer();
        attachToEffectLayer(MainBoardScreen.getScm().getBonusLayer());
        MainBoardScreen.getInstance().getGameSound().playStartSkillSound();
        new ParticleEmitter(10, 50, 0.5f,
                TileSpriteUtil.getTileCenterX(this.getTile().getX()),
                TileSpriteUtil.getTileCenterY(this.getTile().getY()),
                ResourceManager.getInstance().getGoldParticleRegion()
        ).start();
        showStarRotationEffect(1f, TileSpriteUtil.getTileCenterX(this.getTile().getX()),
                TileSpriteUtil.getTileCenterY(this.getTile().getY()) + 5, effectContainer);
        if (abilityType == AbilityType.MOVE_HERO) {
            performMoveHeroAbility(col, row);
        } else if (abilityType == AbilityType.EXPLODE_9) {
            performExplode9Ability(col, row);
        } else if (abilityType == AbilityType.COLLECT_CRYSTALS) {
            performCollectCrystalsAbility(col, row);
        } else if (abilityType == AbilityType.MISSILE) {
            performHitEnemyAbility(col, row);
        } else if (abilityType == AbilityType.PLAY_CONCERT) {
            performPlayConcertAbility(col, row);
        }
    }


    @Override
    public int getManaAmountForMatch() {
        return IConstants.manaUseForMatch * 3;
    }

    private void performPlayConcertAbility(int col, int row) {
        MainBoardScreen.getInstance().getGameSound().setLowMainLoopVolume();
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(1f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().playConcertSkillSound();
            }
        }));
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(2.5f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().playConcertClapSound();
            }
        }));
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(5f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().setDefaultMainLoopVolume();
            }
        }));

        HeroAnimationController.getInstance().animatePlayingConcert();

        LOG.i("perform play concert 2", "");
        final String currentKey = TileBoard.getKey(row, col);

        this.getTile().registerEntityModifier(getSkillActivationEffectModifier(
                new AfterMatchEntityModifierListener(currentKey, this, new Runnable() {
                    @Override
                    public void run() {
                        int townIndex = GameProfile.getCurrentLevelProfile().getReachedTownIndex();
                        int remainingConcerts = GameProfile.getCurrentLevelProfile().getRemainingConcertsByTown().get(townIndex);
                        if (remainingConcerts > 0) {
                            final Entity townJewels = GameProfile.getCurrentLevelProfile().getConcertsByTown().get(townIndex);
                            //flying jewel to the frame
                            final Sprite jewelParticle = TilesPool.getEntityForTexture(ResourceManager.getInstance().getJewelParticle());
                            MainBoardScreen.getScm().getTestLayer().attachChild(jewelParticle);
                            final float jewelStartX = townJewels.getX();
                            final float jewelStartY = CAMERA_HEIGHT - 55;
                            final float[] jewelTargetXY = getJewelTargetXY();
                            float flyDuration = 1f;
                            jewelParticle.setPosition(jewelStartX, jewelStartY);
                            jewelParticle.setAlpha(0f);
                            final int finalRemainingConcerts = remainingConcerts - 1;
                            jewelParticle.registerEntityModifier(
                                    new SequenceEntityModifier(
                                            new DelayModifier(1f),
                                            new ParallelEntityModifier(
                                                    new AlphaModifier(flyDuration / 2, 0, 1, new IEntityModifier.IEntityModifierListener() {
                                                        @Override
                                                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {

                                                            SpriteUtil.revealEntityAndChildren(0.5f, 1, 0, townJewels, new Runnable() {
                                                                @Override
                                                                public void run() {
                                                                    townJewels.detachChildren();
                                                                    if (finalRemainingConcerts > 0) {
                                                                        Entity newJewels = TilesPool.getEntityForTexture(ResourceManager.getInstance().getTownJewels()[finalRemainingConcerts - 1]);
                                                                        townJewels.attachChild(newJewels);
                                                                        SpriteUtil.revealEntityAndChildren(0.5f, 0, 1, townJewels, null);
                                                                    }
                                                                }
                                                            }, false);

                                                            showStarRotationEffect(1f, jewelStartX, jewelStartY,
                                                                    MainBoardScreen.getScm().getResourcePoolsLayer());
                                                        }

                                                        @Override
                                                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                                            jewelParticle.setPosition(jewelTargetXY[0], jewelTargetXY[1]);
                                                            jewelParticle.setVisible(false);
                                                        }
                                                    }),
                                                    new ScaleAtModifier(flyDuration / 2, 1, 2, 0.5f, 0.5f),
                                                    new RotationModifier(flyDuration / 2, 0, -180)
                                            ),
                                            new DelayModifier(flyDuration / 4),
                                            new ParallelEntityModifier(
                                                    new AlphaModifier(flyDuration, 1, 0, new IEntityModifier.IEntityModifierListener() {
                                                        @Override
                                                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                                            jewelParticle.setVisible(true);
                                                            Sprite jewelOnFrame = TilesPool.getEntityForTexture(ResourceManager.getInstance().getFrameJewels()[GameProfile.getCurrentLevelProfile().getPerformedConcerts() - 1]);
                                                            jewelOnFrame.setAnchorCenter(0, 1);
                                                            jewelOnFrame.setPosition(0, CAMERA_HEIGHT);
                                                            showStarRotationEffect(2f, jewelTargetXY[0], jewelTargetXY[1], MainBoardScreen.getScm().getResourcePoolsLayer());
                                                            MainBoardScreen.getScm().getResourcePoolsLayer().attachChild(jewelOnFrame);
                                                            SpriteUtil.revealEntityAndChildren(0.5f, 0, 1, jewelOnFrame, null);
                                                        }

                                                        @Override
                                                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                                            jewelParticle.detachSelf();
                                                            TilesPool.pushEntityToStack(jewelParticle);
                                                        }
                                                    }),
                                                    new ScaleAtModifier(flyDuration, 2, 1, 0.5f, 0.5f),
                                                    new RotationModifier(flyDuration, 0, -180)
                                            )
                                    )
                            );

                            remainingConcerts--;
                            GameProfile.getCurrentLevelProfile().getRemainingConcertsByTown().put(townIndex, remainingConcerts);
                            GameProfile.getCurrentLevelProfile().addPerformedConcert();
                            //ManaAndPointsController.getInstance().updateConcerts();
                            GameProfile.getCurrentLevelProfile().addAchievedPoints(1000, 1, CAMERA_WIDTH / 2, CAMERA_HEIGHT / 2);

                            if (remainingConcerts == 0) {
                                TileBoard.replaceAllAbilitiesForLandscape(false);
                            }

                        }
                    }
                })));
    }

    private float[] getJewelTargetXY() {
        int performedConcertsBefore = GameProfile.getCurrentLevelProfile().getPerformedConcerts();
        float[] result = new float[2];
        if (performedConcertsBefore == 0) {
            result[0] = CAMERA_WIDTH / 2 - 140;
            result[1] = CAMERA_HEIGHT - 50;
        } else if (performedConcertsBefore == 1) {
            result[0] = CAMERA_WIDTH / 2 - 75;
            result[1] = CAMERA_HEIGHT - 20;
        } else if (performedConcertsBefore == 2) {
            result[0] = CAMERA_WIDTH / 2;
            result[1] = CAMERA_HEIGHT - 10;
        } else if (performedConcertsBefore == 3) {
            result[0] = CAMERA_WIDTH / 2 + 75;
            result[1] = CAMERA_HEIGHT - 20;
        } else if (performedConcertsBefore == 4) {
            result[0] = CAMERA_WIDTH / 2 + 140;
            result[1] = CAMERA_HEIGHT - 50;
        }
        return result;
    }

    private void performHitEnemyAbility(int col, int row) {
        HeroAnimationController.getInstance().animateShootingEnemy();
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(1f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().playMissileSkillSound();
            }
        }));


        this.getTile().registerEntityModifier(getSkillActivationEffectModifier(
                new AfterMatchEntityModifierListener(TileBoard.getKey(row, col), this)));
        final float missileX = HeroAnimationController.getInstance().getHeroCurrentX() + 20;
        final float missileY = GROUND_Y + 35;
        final Sprite missile = TilesPool.getEntityForTexture(ResourceManager.getInstance().getMissileRegion());
        missile.setPosition(missileX, missileY);
        missile.setAlpha(0);
        ScreenContentManager.getScreenContentManager().getHeroLayer().attachChild(missile);
        final int targetEnemyIndex = LevelController.getTimeLine().getTargetEnemyIndex();
        final AnimatedSprite targetEnemyImp = LevelController.getTimeLine().getEnemyMap().get(targetEnemyIndex);
        float missileTargetX = CAMERA_WIDTH;
        final boolean targetEnemyVisible = targetEnemyImp != null && targetEnemyImp.getX() < CAMERA_WIDTH;
        if (targetEnemyVisible) {
            missileTargetX = targetEnemyImp.getX();
        }
        float shootDuration = (missileTargetX - missile.getX()) / 300;
        final float finalMissileTargetX = missileTargetX;
        missile.registerEntityModifier(new SequenceEntityModifier(
                new DelayModifier(1.25f),
                new MoveXModifier(shootDuration, missile.getX(), missileTargetX, new IEntityModifier.IEntityModifierListener() {
                    @Override
                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        missile.setAlpha(1);
                        TouchAndMoveController.lockTouchActions();
                    }

                    @Override
                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                        TouchAndMoveController.unlockTouchActions();
                        MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                            @Override
                            public void run() {
                                TilesPool.pushEntityToStack(missile);
                                missile.detachSelf();
                            }
                        });
                        if (targetEnemyVisible) {
                            new ParticleEmitter(5, 25, 0.3f, finalMissileTargetX, missileY,
                                    ResourceManager.getInstance().getGoldParticleRegion(), MainBoardScreen.getInstanceEngine(), MainBoardScreen.getScm().getBonusLayer()).start();

                            //dissolve imp
                            targetEnemyImp.registerEntityModifier(new AlphaModifier(1, 1, 0));
                            //remove imp from the map!
                            ParticleUtil.effectDisappearWithSmoke(targetEnemyImp.getX() + 20, targetEnemyImp.getY() + 30);
                            LevelController.getTimeLine().getEnemyMap().remove(targetEnemyIndex);
                        }
                    }
                })));
    }


    private void performMatchWithEnemyAndFireRemoval(final TileSprite tile, int i, int j, String currentKey) {
        //kill enemy and fire on tiles too!!!
        if (tile.getEnemyImp() != null) {
            final Entity enemyImpCopy = tile.getEnemyImp();
            tile.setEnemyImp(null);
            enemyImpCopy.registerEntityModifier(new ScaleModifier(0.2f, 1f, 2f, new IEntityModifier.IEntityModifierListener() {
                @Override
                public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                }

                @Override
                public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                    enemyImpCopy.registerEntityModifier(new ScaleModifier(0.3f, 2f, 0f, new IEntityModifier.IEntityModifierListener() {
                        @Override
                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        }

                        @Override
                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                @Override
                                public void run() {
                                    enemyImpCopy.detachSelf();
                                }
                            });
                        }
                    }));
                }
            }));

        } else if (tile.getFireOnTile() != null) {
            final Entity fireOnTileCopy = tile.getFireOnTile();
            tile.setFireOnTile(null); //set null now to prevent starting enemy actions!!
            fireOnTileCopy.registerEntityModifier(new ScaleModifier(0.2f, 1f, 3f, new IEntityModifier.IEntityModifierListener() {
                @Override
                public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                }

                @Override
                public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                    fireOnTileCopy.registerEntityModifier(new ScaleModifier(0.2f, 3f, 0f, new IEntityModifier.IEntityModifierListener() {
                        @Override
                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        }

                        @Override
                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                @Override
                                public void run() {
                                    fireOnTileCopy.detachSelf();
                                }
                            });
                        }
                    }));
                }
            }));
        }
        if (this != tile && !TileBoard.tilesInAction.contains(currentKey) && !tile.isMatchActionPerformed()) {
            tile.performMatchAction(i, j);
            if (tile.producesMana() && tile instanceof ManaIncreasingTile) {
                ManaAndPointsController.getInstance().addPartialMana(
                        //GameProfile.getCurrentLevel().getManaSettings().getAmountAddForCrystal()
                        ((ManaIncreasingTile) tile).getManaAmountForMatch()
                );
            }

        }
    }

    private void performExplode9Ability(final int col, final int row) {
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(0.1f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().playExplodeSkillSound();
            }
        }));
        setMatchActionPerformed(true);
        this.getTile().registerEntityModifier(getSkillActivationEffectModifier(
                new AfterMatchEntityModifierListener(TileBoard.getKey(row, col), this, new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < CELLS_HORIZONTAL; i++)
                            for (int j = 0; j < CELLS_VERTICAL; j++) {
                                if (TileBoard.isCellValid(i, j) &&
                                        (i == col || j == row)) {
                                    final String currentKey = TileBoard.getKey(j, i);
                                    AfterMatchProcessor.killedKeys.add(currentKey);
                                    final TileSprite tile = TileBoard.getTileMap().get(currentKey);
                                    tile.setTemporaryMatchEffect(new Runnable() {
                                        @Override
                                        public void run() {
                                            ParticleUtil.effectSparks(0.5f,
                                                    TileSpriteUtil.getTileCenterX(tile.getTile().getX()),
                                                    TileSpriteUtil.getTileCenterY(tile.getTile().getY()));
                                            final Sprite explosion = TilesPool.getEntityForTexture(ResourceManager.getInstance().getEffectExplosion());
                                            explosion.setPosition(TileSpriteUtil.getTileCenterX(tile.getTile().getX()),
                                                    TileSpriteUtil.getTileCenterY(tile.getTile().getY()));
                                            MainBoardScreen.getScm().getBonusLayer().attachChild(explosion);
                                            explosion.registerEntityModifier(new SequenceEntityModifier(
                                                    new AlphaModifier(0.2f, 0, 1),
                                                    new AlphaModifier(0.2f, 1, 0, new IEntityModifier.IEntityModifierListener() {
                                                        @Override
                                                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                                            //To change body of implemented methods use File | Settings | File Templates.
                                                        }

                                                        @Override
                                                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                                                @Override
                                                                public void run() {
                                                                    explosion.detachSelf();
                                                                    TilesPool.pushEntityToStack(explosion);
                                                                    ParticleUtil.effectDisappearWithSmoke(
                                                                            TileSpriteUtil.getTileCenterX(tile.getTile().getX()),
                                                                            TileSpriteUtil.getTileCenterY(tile.getTile().getY()));
                                                                }
                                                            });
                                                        }
                                                    })
                                            ));
                                        }
                                    });
                                    tile.setTemporaryMatchDissappearDelay(0.3f);
                                    tile.setUseDefaultDissolveAction(true);
                                    performMatchWithEnemyAndFireRemoval(tile, i, j, currentKey);
                                }
                            }
                        ManaAndPointsController.getInstance().fireCollectedManaSymbol();
                    }
                })));
    }

    private void performCollectCrystalsAbility(int col, int row) {
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(0.1f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().playMagnetSkillSound();
            }
        }));

        setMatchActionPerformed(true);
        this.getTile().registerEntityModifier(getSkillActivationEffectModifier(
                new AfterMatchEntityModifierListener(TileBoard.getKey(row, col), this, new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < IConstants.CELLS_VERTICAL; i++)
                            for (int j = 0; j < IConstants.CELLS_HORIZONTAL; j++) {
                                if (TileBoard.isCellValid(i, j)) {
                                    final String currentKey = TileBoard.getKey(j, i);
                                    final TileSprite tile = TileBoard.getTileMap().get(currentKey);
                                    if (tile.getStyle() == LandscapeType.CRYSTAL || tile.getStyle() == LandscapeType.BIGCRYSTAL) {
                                        AfterMatchProcessor.killedKeys.add(currentKey);
                                        tile.setTemporaryMatchEffect(new Runnable() {
                                            @Override
                                            public void run() {
                                                float fromX = tile.getTile().getX() + 30;
                                                float fromY = tile.getTile().getY() - 70;
                                                //duplicates method in ManaIncreasingTile class - not neede here
//                                                ParticleUtil.makeCrystalPath(IConstants.CRYSTAL_COLLECT_DURATION, fromX, fromY, ManaAndPointsController.MANA_TARGET_X, ManaAndPointsController.MANA_TARGET_Y);
                                            }
                                        });
                                        performMatchWithEnemyAndFireRemoval(tile, i, j, currentKey);
                                    }
                                }
                            }
                        ManaAndPointsController.getInstance().fireCollectedManaSymbol();
                    }
                })));
    }


    private void performMoveHeroAbility(final int col, final int row) {
        MainBoardScreen.getScm().getMainScene().registerUpdateHandler(new TimerHandler(0.5f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                MainBoardScreen.getInstance().getGameSound().playHasteSkillSound();
            }
        }));


        final TileSprite thisTile = this;
        //only for defining the hero position
        final String currentKey = TileBoard.getKey(row, col);
        SymbolAnimationUtil.moveSymbolUp(
                TileSpriteUtil.getTileCenterX(this.getTile().getX()),
                TileSpriteUtil.getTileCenterY(this.getTile().getY()),
                SpriteUtil.getMoveHeroSymbolSprite());
        this.getTile().registerEntityModifier(getSkillActivationEffectModifier(
                new AfterMatchEntityModifierListener(currentKey, this, new Runnable() {
                    @Override
                    public void run() {
                        LevelController.getTimeLine().proceedLevelSteps(3);
                    }
                })));
    }

    public AbilityType getOriginalAbility() {
        return originalAbility;
    }

    public void setOriginalAbility(AbilityType originalAbility) {
        this.originalAbility = originalAbility;
    }

    public AbilityType getAbilityType() {
        return abilityType;
    }

    public void setAbilityType(AbilityType abilityType) {
        this.abilityType = abilityType;
    }

    private IEntityModifier getSkillActivationEffectModifier(AfterMatchEntityModifierListener listener) {
        return new SequenceEntityModifier(
                new ParallelEntityModifier(
                        new AlphaModifier(DURATION_SCALE_UP_CRYSTAL_ON_ACTIVATE, 1, 0.5f, new IEntityModifier.IEntityModifierListener() {
                            @Override
                            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                SpriteUtil.revealEntityAndChildren(pModifier.getDuration(), 1, 0.5f, pItem, null, false);
                            }

                            @Override
                            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                //To change body of implemented methods use File | Settings | File Templates.
                            }
                        }),
                        new ScaleAtModifier(DURATION_SCALE_UP_CRYSTAL_ON_ACTIVATE, 1, 1.5f, 0.5f, 0.5f)),
                new ParallelEntityModifier(
                        new AlphaModifier(DURATION_FADE_OUT_CRYSTAL_ON_ACTIVATE, 0.5f, 0f, new IEntityModifier.IEntityModifierListener() {
                            @Override
                            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                SpriteUtil.revealEntityAndChildren(pModifier.getDuration(), 0.5f, 0f, pItem, null, false);
                            }

                            @Override
                            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                //To change body of implemented methods use File | Settings | File Templates.
                            }
                        }),
                        new DelayModifier(0.1f, listener),
                        new ScaleAtModifier(DURATION_FADE_OUT_CRYSTAL_ON_ACTIVATE, 1.5f, 0.5f, 0.5f, 0.5f)));
    }

    @Override
    protected void performMatchAdditionalActions(int col, int row) {
        final float fromX = this.getTile().getX() + 30;
        final float fromY = this.getTile().getY() - 70;
        ManaAndPointsController.getInstance().updateMana();

        showStarRotationEffect(0.5f, TileSpriteUtil.getTileCenterX(this.getTile().getX()),
                TileSpriteUtil.getTileCenterY(this.getTile().getY()) + 5, effectContainer);


        for (int i = 0; i < 3; i++) {
            final Sprite crystal = TilesPool.getEntityForTexture(ResourceManager.getInstance().getMagicTileTextureRegion());
            final float jumpY = fromY + (i == 1 ? 60 : 30);
            final float jumpX = fromX - 25 + 25 * i;
            MainBoardScreen.getScm().getBonusLayer().attachChild(crystal);
            crystal.registerEntityModifier(new SequenceEntityModifier(
                    new DelayModifier(0.1f * i),
                    new MoveSmoothModifier(0.5f, fromX, fromY, jumpX, jumpY),
                    new DelayModifier(0.2f, new IEntityModifier.IEntityModifierListener() {
                        @Override
                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                            showStarRotationEffect(0.6f, 0.5f, jumpX, jumpY, MainBoardScreen.getScm().getBonusLayer());
                        }

                        @Override
                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                            ParticleUtil.makeCrystalPath(IConstants.CRYSTAL_COLLECT_DURATION / 2, jumpX, jumpY, ManaAndPointsController.MANA_TARGET_X, ManaAndPointsController.MANA_TARGET_Y);
                            ParticleUtil.makeParticlePath(jumpX, jumpY, ManaAndPointsController.MANA_TARGET_X, ManaAndPointsController.MANA_TARGET_Y,
                                    ResourceManager.getInstance().getParticleRegion());

                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                @Override
                                public void run() {
                                    TilesPool.pushEntityToStack(crystal);
                                    crystal.detachSelf();
                                }
                            });
                        }
                    })));


        }

    }

}
